/**
 * 前 K 个高频元素
 *
 * 给你一个整数数组 nums 和一个整数 k ，请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
 *
 * 示例 1：
 * 输入：nums = [1,1,1,2,2,3], k = 2
 * 输出：[1,2]
 *
 * 示例 2：
 * 输入：nums = [1], k = 1
 * 输出：[1]
 *
 * 示例 3：
 * 输入：nums = [1,2,1,2,1,2,3,1,3,2], k = 2
 * 输出：[1,2]
 *
 * 提示：
 * 1 <= nums.length <= 105
 * k 的取值范围是 [1, 数组中不相同的元素的个数]
 * 题目数据保证答案唯一，换句话说，数组中前 k 个高频元素的集合是唯一的
 *
 * 进阶：你所设计算法的时间复杂度 必须 优于 O(n log n) ，其中 n 是数组大小。
 */

import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;

/**
 * 个人感觉这题的思路是挺重要的, 首先就是使用 Map 存储
 * 相关东西出现的个数, 然后就是在存储的时候使用 Map 中存储
 * 的个数进行判断谁他们的优先级
 * 当然求 前 K 个当然还是使用堆了, 前 k 大个就是用小根堆
 * 时间复杂度 : O(n log(n)
 * 空间复杂度 : O(n)
 */


public class Main {
    public int[] topKFrequent(int[] nums, int k) {

        // map
        Map<Integer, Integer> hash = new HashMap<>();

        // 存储各自出现的个数
        for (int i = 0; i < nums.length; i++) {
            hash.put(nums[i], hash.getOrDefault(nums[i], 0) + 1);
        }

        // 这里构建小根堆, 排序的规则是和 map 中存储的个数来的
        PriorityQueue<Integer> queue = new PriorityQueue<>((a, b) -> hash.get(a) - hash.get(b));

        // 遍历 map 中的 key
        for (Integer x : hash.keySet()) {

            if (queue.size() < k) {

                // 要是堆中的个数小于 k, 直接加入
                queue.add(x);
            } else if (hash.get(x) > hash.get(queue.peek())) {

                // 大与等于的话, 就要先出一个元素在加入了,
                // 是否出元素取决于是否比堆的第一个元素大
                // 大的话就加入
                queue.poll();
                queue.add(x);
            }
        }

        // 将结果存入 数组中
        int[] arr = new int[k];
        int i = 0;

        while (!queue.isEmpty()) {
            arr[i++] = queue.poll();
        }

        // 返回结果
        return arr;
    }

    
}